﻿using BeetleX.Buffers;
using BeetleX.EventArgs;
using System;
using System.IO;

#nullable disable
namespace BeetleX.XRPC.Packets
{
    public class ServerPacket : IPacket, IDisposable
    {
        private RPCPacket mRequest;
        private PacketDecodeCompletedEventArgs mCompletedArgs = new PacketDecodeCompletedEventArgs();

        public EventHandler<PacketDecodeCompletedEventArgs> Completed { get; set; }

        public IPacket Clone()
        {
            return (IPacket)new ServerPacket()
            {
                Options = this.Options
            };
        }

        public Options Options { get; internal set; }

        public void Decode(ISession session, Stream stream)
        {
            PipeStream pipeStream = stream.ToPipeStream();
            while (true)
            {
                if (this.mRequest == null)
                {
                    this.mRequest = new RPCPacket();
                    this.mRequest.Session = session;
                }
                if (this.mRequest.Read(this.Options, pipeStream))
                {
                    this.mCompletedArgs.SetInfo(session, this.mRequest);
                    try
                    {
                        EventHandler<PacketDecodeCompletedEventArgs> completed = this.Completed;
                        if (completed != null)
                            completed(this, this.mCompletedArgs);
                    }
                    finally
                    {
                        //mRequest?.Dispose();
                        this.mRequest = null;
                    }
                }
                else
                    break;
            }
        }

        public void Dispose() => this.mCompletedArgs = null;

        public void Encode(object data, ISession session, Stream stream)
        {
            this.OnEncode(session, data, stream);
        }

        private void OnEncode(ISession session, object data, Stream stream)
        {
            PipeStream pipeStream = stream.ToPipeStream();
            (data as RPCPacket).Write(this.Options, pipeStream);
        }

        public byte[] Encode(object data, IServer server)
        {
            byte[] buffer = (byte[])null;
            using (PipeStream pipeStream = new PipeStream(server.SendBufferPool.Next(), server.Options.LittleEndian, server.Options.Encoding))
            {
                this.OnEncode((ISession)null, data, (Stream)pipeStream);
                pipeStream.Position = 0L;
                buffer = new byte[pipeStream.Length];
                pipeStream.Read(buffer, 0, buffer.Length);
            }
            return buffer;
        }

        public ArraySegment<byte> Encode(object data, IServer server, byte[] buffer)
        {
            using (PipeStream pipeStream = new PipeStream(server.SendBufferPool.Next(), server.Options.LittleEndian, server.Options.Encoding))
            {
                this.OnEncode((ISession)null, data, (Stream)pipeStream);
                pipeStream.Position = 0L;
                int length = (int)pipeStream.Length;
                pipeStream.Read(buffer, 0, length);
                return new ArraySegment<byte>(buffer, 0, length);
            }
        }
    }
}
